using System;
using System.Collections;
using System.Collections.Generic;

namespace Framework
{
	public static class NumUtil
    {
        public static int INVALID_INT = int.MinValue + 1;

        public static float INVALID_FLOAT = float.MinValue + 1;

        public static decimal INVALID_DECIMAL = decimal.MinValue + 1;

        /*
         * Notes: An alternate to Int32.Parse etc... is to use the System.Convert class 
	     * System.Convert is going to be slower because it looks up the type of the object to convert
	     */

        // *********************************************************************************
        // Parse strings to numbers
        // *********************************************************************************

		#region ParseInt
        /// <summary>
        /// Convert a String representation of a number to an int value.
        /// <p>
        /// Badly formated, null or empty string will be return a either a user passed default value or -1.
        /// </p>
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>int value</returns>
		public static int ParseInt(String value)
		{
			return ParseInt(value, -1);
		}

        /// <summary>
        /// Convert a String representation of a number to an int value.
        /// <p>
        /// Badly formated, null or empty string will be return a either a user passed default value or -1.
        /// </p>
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="defValue">The def value.</param>
        /// <returns>int value</returns>
		public static int ParseInt(string value, int defValue)
		{
			try
			{
                if (StringEquivalentOfEmptyNumber(value))
                {
                    return defValue;
                }

			    int result;

			    var isNum = int.TryParse(value, out result);

			    return isNum ? result : defValue;
			}
			catch (Exception)
			{
				return defValue;
			}
		}
		#endregion

		#region ParseNullableInt
        /// <summary>
        /// Convert a String representation of a number to an int value.
        /// <p>
        /// Badly formated, null or empty string will be return as null
        /// </p>
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>int value</returns>
        public static int? ParseNullableInt(String value)
		{
            if (StringEquivalentOfEmptyNumber(value))
            {
                return null;
            }

		    int result;

		    if (int.TryParse(value, out result))
		    {
		        return result;
		    }

		    return null;
		}
		#endregion

		#region ParseDouble
        /// <summary>
        /// Convert a String representation of a number to a double value.
        /// <p>
        /// Badly formated, null or empty string will be return a either a user passed default value or -1.0
        /// </p>
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>int value</returns>
		public static double ParseDouble(String value)
		{
			return ParseDouble(value, -1.0);
		}

        /// <summary>
        /// Convert a String representation of a number to an double value.
        /// <p>
        /// Badly formated, null or empty string will be return a either a user passed default value or -1.0
        /// </p>
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="defValue">The def value.</param>
        /// <returns>double value</returns>
		public static double ParseDouble(String value, double defValue)
		{
			try
			{
                if (StringEquivalentOfEmptyNumber(value))
                {
                    return defValue;
                }

                double result;

                var isNum = double.TryParse(value, out result);

                return isNum ? result : defValue;
			}
			catch (Exception)
			{
				return defValue;
			}
		}
		#endregion

		#region ParseDecimal
        /// <summary>
        /// Convert a String representation of a number to a decimal value.
        /// <p>
        /// Badly formated, null or empty string will be return a either a user passed default value or -1.0
        /// </p>
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>decimal value</returns>
		public static decimal ParseDecimal(String value)
		{
			return ParseDecimal(value, -1);
		}

        /// <summary>
        /// Convert a String representation of a number to an decimal value.
        /// <p>
        /// Badly formated, null or empty string will be return a either a user passed default value or -1.0
        /// </p>
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="defValue">The def value.</param>
        /// <returns>double value</returns>
		public static decimal ParseDecimal(String value, decimal defValue)
		{
			try
			{
                if (StringEquivalentOfEmptyNumber(value))
                {
                    return defValue;
                }

                decimal result;

                var isNum = decimal.TryParse(value, out result);

                return isNum ? result : defValue;
			}
			catch (Exception)
			{
				return defValue;
			}
		}
		#endregion

        #region ParseFloat
        /// <summary>
        /// Convert a String representation of a number to a float value.
        /// <p>
        /// Badly formated, null or empty string will be return a either a user passed default value or -1.0
        /// </p>
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>int value</returns>
        public static float ParseFloat(String value)
        {
            return ParseFloat(value, -1.0F);
        }

        /// <summary>
        /// Convert a String representation of a number to an float value.
        /// <p>
        /// Badly formated, null or empty string will be return a either a user passed default value or -1.0
        /// </p>
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="defValue">The def value.</param>
        /// <returns>float value</returns>
        public static float ParseFloat(String value, float defValue)
        {
            try
            {
                if (StringEquivalentOfEmptyNumber(value))
                {
                    return defValue;
                }

                float result;

                var isNum = float.TryParse(value, out result);

                return isNum ? result : defValue;
            }
            catch (Exception)
            {
                return defValue;
            }
        }
        #endregion

		#region ParseLong
        /// <summary>
        /// Convert a String representation of a number to an long value.
        /// <p>
        /// Badly formated, null or empty string will be return a either a user passed default value or -1.
        /// </p>
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>long value</returns>
		public static long ParseLong(String value)
		{
			return ParseLong(value, -1);
		}

        /// <summary>
        /// Convert a String representation of a number to an long value.
        /// <p>
        /// Badly formated, null or empty string will be return a either a user passed default value or -1.
        /// </p>
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="defValue">The def value.</param>
        /// <returns>long value</returns>
		public static long ParseLong(String value, long defValue)
		{
			try
			{
                if (StringEquivalentOfEmptyNumber(value))
                {
                    return defValue;
                }

                long result;

                var isNum = long.TryParse(value, out result);

                return isNum ? result : defValue;
			}
			catch (Exception)
			{
				return defValue;
			}
		}
		#endregion

        // *********************************************************************************
        // Parse objects to numbers 
        // *********************************************************************************

		#region ToInt32
		/// <summary>
		/// Effectivly the same as Convert.ToInt32, except supports a default value for null objects.
		/// </summary>
		/// <param name="value">object containing int value</param>
		/// <param name="defValue">default value to use if value is null</param>
		/// <returns></returns>
		public static int ToInt32(object value, int defValue)
		{
			if (value == null)
			{
				return defValue;
			}

			try
			{
				return ((IConvertible)value).ToInt32(null);
			}
			catch (Exception)
			{
				return defValue;
			}
		}
		#endregion

        // *********************************************************************************
        // Convert numbers to numbers of different type
        // *********************************************************************************

        #region DecimalToInt
        public static int DecimalToInt(decimal value)
        {
            return DecimalToInt(value, -1);
        }

        public static int DecimalToInt(decimal value, int defaultValue)
        {
            try
            {
                return Convert.ToInt32(value);
            }
            catch (Exception)
            {
                return defaultValue;
            }
        }
        #endregion

        #region DecimalToLong
        public static long DecimalToLong(decimal value)
        {
            return DecimalToLong(value, -1);
        }

        public static long DecimalToLong(decimal value, long defaultValue)
        {
            try
            {
                return Convert.ToInt64(value);
            }
            catch (Exception)
            {
                return defaultValue;
            }
        }
        #endregion

        #region SplitAsIntList
        /// <summary>
        /// Split a string into a List&lt;int&gt;, check 1st to see if the string is empty and if 
        /// it is then return an empty List. By default each element is trimmed of whitespace.
        /// <br /><br />
        /// Defaults to spliting comma delimited strings
        /// </summary>
        /// <param name="v">String to split</param>
        /// <returns>List&lt;string&gt;</returns>
        public static List<int> SplitAsIntList(string v)
        {
            return SplitAsIntList(v, ',');
        }

        /// <summary>
        /// Split a string into a List&lt;int&gt;, check 1st to see if the string is empty and if 
        /// it is then return an empty List. By default each element is trimmed of whitespace.
        /// </summary>
        /// <param name="v">String to split</param>
        /// <param name="seperator">seperator to split on</param>
        /// <returns>List&lt;int&gt;</returns>
        public static List<int> SplitAsIntList(string v, params char[] seperator)
        {
            if (Is.EmptyString(v))
            {
                return new List<int>();
            }

            var result = v.Split(seperator);
            var resultList = new List<int>();

            for (var i = 0; i < result.Length; i++)
            {
                resultList.Add(ParseInt(result[i].Trim()));
            }

            return resultList;
        }

        /// <summary>
        /// Split a string into a List&lt;int&gt;, check 1st to see if the string is empty and if 
        /// it is then return an empty List. By default each element is trimmed of whitespace.
        /// </summary>
        /// <param name="v">String to split</param>
        /// <param name="seperator">String seperator to split on</param>
        /// <returns>List&lt;int&gt;</returns>
        public static List<int> SplitAsIntList(string v, params string[] seperator)
        {
            return SplitAsIntList(v, seperator);
        }

        /// <summary>
        /// Split a string into a List&lt;int&gt;, check 1st to see if the string is empty and if 
        /// it is then return an empty List.
        /// </summary>
        /// <param name="trim">If true, each element is trimmed of whitespace.</param>
        /// <param name="v">String to split</param>
        /// <param name="seperator">String seperator to split on</param>
        /// <returns>List&lt;int&gt;</returns>
        public static List<int> SplitAsIntList(bool trim, string v, params string[] seperator)
        {
            if (Is.EmptyString(v))
            {
                return new List<int>();
            }

            var result = v.Split(seperator, StringSplitOptions.None);

            var resultList = new List<int>();

            for (var i = 0; i < result.Length; i++)
            {
                resultList.Add(ParseInt(result[i].Trim()));
            }

            return resultList;
        }
        #endregion


        // *********************************************************************************
        // Cleanup numeric strings
        // *********************************************************************************

        #region StripNumericTokens
        public static string StripNumericTokens(string value)
        {
            return value == null ? null : value.Replace("$", "").Replace("%", "");
        }
        #endregion

        // *********************************************************************************
        // Helpers
        // *********************************************************************************

        #region StringEquivalentOfEmptyNumber
        private static bool StringEquivalentOfEmptyNumber(string value)
        {
            return Is.EmptyString(value) || Is.EqualString(value, "-") || Is.EqualString(value, "na") || Is.EqualString(value, "n/a") || Is.EqualString(value, "NaN");
        }
        #endregion

		//#region Add
		//public static T Add<T>(T v1, T v2)
		//{
		//   return v1 + v2;
		//}
		//#endregion
	}
}
